package com.ganmiao.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ganmiao.exception.DefaultBusinessException;
import lombok.extern.log4j.Log4j2;
import org.apache.http.*;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.*;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.DefaultCookieSpecProvider;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

/**
 * @Author zero.yin
 * @Date 2022/8/6 5:02 PM
 * @Desc http工具类
 */
@Log4j2
public class HttpUtil {

    // 默认连接超时
    private static int defaultConnectionRequestTimeout = 10 * 1000;

    // 默认从连接池中取连接的超时时间
    private static int defaultConnectTimeout = 10 * 1000;

    // 默认请求超时
    private static int defaultSocketTimeout = 30 * 1000;

    private static PoolingHttpClientConnectionManager connectionManager = null;

    private static CloseableHttpClient client;

    private static RequestConfig defaultRequestConfig;

    // 默认cookieStore存放cookie信息
    private static BasicCookieStore cookieStore;

    static {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                }

                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                }
            }}, new SecureRandom());

            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)).build();
            // 创建连接池
            connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            // 配置socket
            SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
            connectionManager.setDefaultSocketConfig(socketConfig);
            // 设置消息限制
            MessageConstraints messageConstraints = MessageConstraints.custom().setMaxHeaderCount(200).setMaxLineLength(20000)
                    .build();
            // 设置连接配置
            ConnectionConfig connectionConfig = ConnectionConfig.custom().setMessageConstraints(messageConstraints)
                    .setMalformedInputAction(CodingErrorAction.IGNORE).setUnmappableInputAction(CodingErrorAction.IGNORE)
                    .setCharset(Consts.UTF_8).build();
            connectionManager.setDefaultConnectionConfig(connectionConfig);
            // 设置连接池数量
            connectionManager.setMaxTotal(200);
            connectionManager.setDefaultMaxPerRoute(20);
            // 设置cookie存储
            cookieStore = new BasicCookieStore();

            Registry<CookieSpecProvider> cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
                    .register(CookieSpecs.DEFAULT, new DefaultCookieSpecProvider()).build();
            client = HttpClients.custom().setDefaultCookieStore(cookieStore).setDefaultCookieSpecRegistry(cookieSpecRegistry)
                    .setConnectionManager(connectionManager).build();
            defaultRequestConfig = RequestConfig.custom().setConnectionRequestTimeout(defaultConnectionRequestTimeout)
                    .setConnectTimeout(defaultConnectTimeout).setSocketTimeout(defaultSocketTimeout).build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 发送GET请求
     */
    public static String doGet(String url) {
        return doGet(url, null, null);
    }

    /**
     * 发送GET请求（带参数）
     */
    public static String doGet(String url, Map<String, Object> params) {
        return doGet(url, params, null);
    }

    /**
     * 发送GET请求（带参数和头）
     */
    public static String doGet(String url, Map<String, Object> params, Map<String, String> headers) {
        HttpGet request = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
            URIBuilder builder = new URIBuilder(url);
            if (params != null && !params.isEmpty()) {
                builder.setParameters(paramsConverter(params));
            }
            request = new HttpGet(builder.build());
            buildHttpRequestConfig(request, headers);
            response = client.execute(request);
            int code = response.getStatusLine().getStatusCode();
            if (code == 200) {    //请求成功
                entity = response.getEntity();
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            } else {
                throw new DefaultBusinessException("远程服务网络异常！code=" + code);
            }
        } catch (Exception e) {
            log.error(String.format("调用[%s]失败！", url), e);
            throw new DefaultBusinessException("远程服务网络异常！");
        } finally {
            close(entity, request, response);
        }
    }

    /**
     * 发送POST请求
     */
    public static String doPost(String url) {
        return doPost(url, null, null);
    }

    /**
     * 发送POST请求
     */
    public static String doPost(String url, Map<String, Object> params) {
        return doPost(url, params, null);
    }

    /**
     * 发送POST请求（带参数和头）
     */
    public static String doPost(String url, Map<String, Object> params, Map<String, String> headers) {
        HttpPost request = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
            URIBuilder builder = new URIBuilder(url);
            request = new HttpPost(builder.build());
            buildHttpRequestConfig(request, headers);
            if (params != null && !params.isEmpty()) {
                request.setEntity(new UrlEncodedFormEntity(paramsConverter(params), StandardCharsets.UTF_8));
            }
            response = client.execute(request);
            int code = response.getStatusLine().getStatusCode();
            if (code == 200) {    //请求成功
                entity = response.getEntity();
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            } else {
                throw new DefaultBusinessException("远程服务网络异常！code=" + code);
            }
        } catch (Exception e) {
            log.error(String.format("调用[%s]失败！", url), e);
            throw new DefaultBusinessException("远程服务网络异常！");
        } finally {
            close(entity, request, response);
        }
    }

    /**
     * jsonPost
     * @param url
     * @param json
     * @return
     */
    public static JSONObject doPost(String url, JSONObject json){

        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        JSONObject response = null;
        try {
            StringEntity s = new StringEntity(json.toJSONString(),"utf-8");
            s.setContentEncoding("UTF-8");
            s.setContentType("application/json");//发送json数据需要设置contentType
            post.setEntity(s);
            HttpResponse res = httpclient.execute(post);
            if(res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                String result = EntityUtils.toString(res.getEntity());// 返回json格式：
                response = JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return response;
    }

    public static JSONObject doPost(String url, JSONArray json){

        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost post = new HttpPost(url);
        JSONObject response = null;
        try {
            StringEntity s = new StringEntity(json.toJSONString(),"utf-8");
            s.setContentEncoding("UTF-8");
            s.setContentType("application/json");//发送json数据需要设置contentType
            post.setEntity(s);
            HttpResponse res = httpclient.execute(post);
            if(res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                String result = EntityUtils.toString(res.getEntity());// 返回json格式：
                response = JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return response;
    }
    /**
     * 上传
     *
     * @return
     */
    public static String upload(String url, String fileName, InputStream inputStream) {
        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            httpPost = new HttpPost(uriBuilder.build());

            String boundary = UUID.randomUUID().toString();
            httpPost.setHeader("Content-Type", "multipart/form-data;boundary=" + boundary);
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addBinaryBody("data", inputStream, ContentType.create("multipart/form-data", Consts.UTF_8), fileName);
            builder.setBoundary(boundary);
            HttpEntity reqEntity = builder.build();
            httpPost.setEntity(reqEntity);
            response = client.execute(httpPost);
            log.info("upload resp=" + JSON.toJSONString(response));
            int code = response.getStatusLine().getStatusCode();
            if (code == 200) {    //请求成功
                entity = response.getEntity();
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            } else {
                throw new DefaultBusinessException("远程服务网络异常！code=" + code);
            }
        } catch (Exception e) {
            log.error(String.format("调用[%s]失败！", url), e);
            throw new DefaultBusinessException("远程服务网络异常！");
        } finally {
            close(entity, httpPost, response);
        }
    }

    private static void buildHttpRequestConfig(HttpRequestBase request, Map<String, String> headers) {
        if (headers == null || headers.isEmpty()) {
            return;
        }
        request.setConfig(defaultRequestConfig);
        request.addHeader(HttpHeaders.ACCEPT, "*/*");
        request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8");
        request.addHeader(HttpHeaders.USER_AGENT,
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36");
        if (headers != null && !headers.isEmpty()) {
            for (Entry<String, String> header : headers.entrySet()) {
                request.addHeader(header.getKey(), header.getValue());
            }
        }
        if (request.getHeaders(HttpHeaders.CONTENT_TYPE).length < 1) {
            request.addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
        }
    }

    private static List<NameValuePair> paramsConverter(Map<String, Object> params) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<Entry<String, Object>> paramsSet = params.entrySet();
        for (Entry<String, Object> paramEntry : paramsSet) {
            nvps.add(new BasicNameValuePair(paramEntry.getKey(), paramEntry.getValue().toString()));
        }
        return nvps;
    }

    private static void close(HttpEntity entity, HttpRequestBase request, CloseableHttpResponse response) {
        try {
            if (request != null) {
                request.releaseConnection();
            }
            if (entity != null) {
                EntityUtils.consume(entity);
            }
            if (response != null) {
                response.close();
            }
        } catch (Exception e) {
        }
    }

    public static int doCode(String url) {
        return getCode(url, null, null);
    }

    public static int getCode(String url, Map<String, Object> params, Map<String, String> headers) {
        HttpGet request = null;
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
            URIBuilder builder = new URIBuilder(url);
            if (params != null && !params.isEmpty()) {
                builder.setParameters(paramsConverter(params));
            }
            request = new HttpGet(builder.build());
            buildHttpRequestConfig(request, headers);
            response = client.execute(request);
            int code = response.getStatusLine().getStatusCode();
            return code;
        } catch (Exception e) {
            log.error(String.format("调用[%s]失败！", url), e);
            throw new DefaultBusinessException("远程服务网络异常！");
        } finally {
            close(entity, request, response);
        }
    }

}
